/*
    Copyright (C) 2009 Giacomo Spigler

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#include <cutil.h>
#include <cuda_runtime.h>


#include <cv.h>
#include <highgui.h>


#include "../cuda/standard.h"
#include "../cuda/gabor.h"



#define min(a, b) (a>=b?b:a)

#define writebyte(a__, fp__) fwrite(&a__, sizeof(unsigned char), 1, fp__);
#define writeint(a__, fp__) fwrite(&a__, sizeof(int), 1, fp__);
#define writefloat(a__, fp__) fwrite(&a__, sizeof(float), 1, fp__)

#define readbyte(a__, fp__) fread(&a__, sizeof(unsigned char), 1, fp__);
#define readint(a__, fp__) fread(&a__, sizeof(int), 1, fp__);
#define readfloat(a__, fp__) fread(&a__, sizeof(float), 1, fp__)






int main() {
  srand((unsigned)(time(0)));


  IplImage *im=cvLoadImage("../imgs/car_0001.ppm.png");



/*
  FILE *fp=fopen("s2.patch", "w");
  writeint(n4, fp);
  writeint(n8, fp);
  writeint(nm, fp);
  writeint(nm, fp);
*/

  #ifdef DEBUG
    unsigned int hTimer;
    cutCreateTimer(&hTimer);
    cutStartTimer(hTimer);
  #endif


  int w_host[NUMSCALES], h_host[NUMSCALES];

  IplImage *pyr[NUMSCALES];
  pyr[0]=cvCreateImage(cvSize(im->width, im->height), 8, 1);
  w_host[0]=im->width; h_host[0]=im->height;
  for(int i=1; i<NUMSCALES; i++) {
    pyr[i]=cvCreateImage(cvSize(pyr[i-1]->width/INVERSESCALERATIO, pyr[i-1]->height/INVERSESCALERATIO), 8, 1);

    w_host[i]=pyr[i]->width; h_host[i]=pyr[i]->height;
  }

  int *w_dev, *h_dev;
  InitWidthsHeights(w_host, h_host, &w_dev, &h_dev);

  unsigned char *pyr_dev;
  unsigned char *s1_dev[4];
  unsigned char *c1_dev[4];
  unsigned int pyr_dev_pitch;
  unsigned int c1_pitch;

  int nums2=0;
  int n4=0;
  int n8=0;
  unsigned char *patch4x4; //device
  unsigned char *patch8x8; //device

  unsigned char *s2_dev; //device
  unsigned int s2_pitch;

  InitPyramidBuffer(pyr, &pyr_dev, s1_dev, c1_dev, &pyr_dev_pitch, &c1_pitch, &s2_dev, nums2, &s2_pitch);

  #ifdef DEBUG
    cutStopTimer(hTimer);
    printf("Initialization time: %fms\n", cutGetTimerValue(hTimer));
  #endif



//  IplImage *c0=cvCreateImage(cvSize(im->width, im->height), 8, 1);
//  IplImage *c1=cvCreateImage(cvSize(im->width, im->height), 8, 1);
//  IplImage *c2=cvCreateImage(cvSize(im->width, im->height), 8, 1);
//  IplImage *c3=cvCreateImage(cvSize(im->width, im->height), 8, 1);

    IplImage *c1[NUMSCALES-1];
    for(int i=0; i<NUMSCALES-1; i++) {
      int c1_w=(pyr[i]->width-2*GABOR_W-C1_SIZE)/C1_STEP+1;
      int c1_h=(pyr[i]->height-2*GABOR_W-C1_SIZE)/C1_STEP+1;

      c1[i]=cvCreateImage(cvSize(c1_w, c1_h), 8, 1);
    }



    int w_0=(w_host[0]-2*GABOR_W-C1_SIZE)/C1_STEP + 1;
    int h_0=(h_host[0]-2*GABOR_W-C1_SIZE)/C1_STEP + 1;




/*
  for(int i=0; i<n4+n8; i++) {
    printf("%f\n", (float)i/(float)(n4+n8));


    int sz=4;

    if(i<n4) sz=4;
    else sz=8;


    int rnd=(int)(fabs((float)rand()/(float(RAND_MAX)+1.0))*516)+1;
    int scale=(int)(fabs((float)rand()/(float(RAND_MAX)+1.0))*9);

    int w=(w_host[scale]-2*GABOR_W-C1_SIZE)/C1_STEP + 1;
    int h=(h_host[scale]-2*GABOR_W-C1_SIZE)/C1_STEP + 1;

    while(min(w, h)<sz) {
      scale=(int)(fabs((float)rand()/(float(RAND_MAX)+1.0))*9);

      w=(w_host[scale]-2*GABOR_W-C1_SIZE)/C1_STEP + 1;
      h=(h_host[scale]-2*GABOR_W-C1_SIZE)/C1_STEP + 1;
    }


    char buf[80];
    if(rnd<10) sprintf(buf, "../imgs/car_000%d.ppm.png", rnd);
    else if(rnd<100) sprintf(buf, "../imgs/car_00%d.ppm.png", rnd);
    else sprintf(buf, "../imgs/car_0%d.ppm.png", rnd);


    int x=(int)(fabs((float)rand()/(float(RAND_MAX)+1.0))*(w-sz));
    int y=(int)(fabs((float)rand()/(float(RAND_MAX)+1.0))*(h-sz));

    //buf, scale, x, y, sz


    im=cvLoadImage(buf);

    SetPyramid_0(im, pyr, pyr_dev, pyr_dev_pitch);
    ComputeS1_1(pyr_dev, s1_dev, w_host, h_host, w_dev, h_dev, pyr_dev_pitch,   pyr);
    ComputeC1_2(s1_dev, c1_dev, w_host, h_host, w_dev, h_dev, pyr_dev_pitch,   pyr);



    int w_0=(w_host[0]-2*GABOR_W-C1_SIZE)/C1_STEP + 1;
    int h_0=(h_host[0]-2*GABOR_W-C1_SIZE)/C1_STEP + 1;

    cudaMemcpy2D(c0->imageData, c0->widthStep, c1_dev[0]+scale*w_0*h_0, w_0, w, h, cudaMemcpyDeviceToHost);
    cudaMemcpy2D(c1->imageData, c1->widthStep, c1_dev[1]+scale*w_0*h_0, w_0, w, h, cudaMemcpyDeviceToHost);
    cudaMemcpy2D(c2->imageData, c2->widthStep, c1_dev[2]+scale*w_0*h_0, w_0, w, h, cudaMemcpyDeviceToHost);
    cudaMemcpy2D(c3->imageData, c3->widthStep, c1_dev[3]+scale*w_0*h_0, w_0, w, h, cudaMemcpyDeviceToHost);


    for(int j=0; j<sz; j++) {
      for(int k=0; k<sz; k++) {
        unsigned char a, b, c, d;
        a=c0->imageData[(y+k)*c0->widthStep+(x+j)];
        b=c1->imageData[(y+k)*c1->widthStep+(x+j)];
        c=c2->imageData[(y+k)*c2->widthStep+(x+j)];
        d=c3->imageData[(y+k)*c3->widthStep+(x+j)];

        writebyte(a, fp);
        writebyte(b, fp);
        writebyte(c, fp);
        writebyte(d, fp);
      }
    }
*/


    char str[80];
    char str2[80];

    char ifile[80];
    char ofile[80];

    for(int o=1; o<=516; o++) {
      printf(">> %d/516\n", o);

      sprintf(str, "%d", o);
      int len=3-strlen(str);
      if(len==2) sprintf(str2, "car_000");
      else if(len==1)  sprintf(str2, "car_00");
      else  sprintf(str2, "car_0");

      sprintf(str, "%s%d", str2, o);
      sprintf(ifile, "../imgs/%s.ppm.png", str);
      sprintf(ofile, "c1/%s", str);


      im=cvLoadImage(ifile);
      SetPyramid_0(im, pyr, pyr_dev, pyr_dev_pitch);
      ComputeS1_1(pyr_dev, s1_dev, w_host, h_host, w_dev, h_dev, pyr_dev_pitch,   pyr);
      ComputeC1_2(s1_dev, c1_dev, w_host, h_host, w_dev, h_dev, pyr_dev_pitch, c1_pitch,   pyr);


      for(int k=0; k<NUMSCALES-1; k++) {
        int w=(w_host[k]-2*GABOR_W-C1_SIZE)/C1_STEP + 1;
        int h=(h_host[k]-2*GABOR_W-C1_SIZE)/C1_STEP + 1;

        for(int ori=0; ori<4; ori++) {
          char tmp[80];
          sprintf(tmp, "%sS%dO%d.png", ofile, k, ori);

          cudaMemcpy2D(c1[k]->imageData, c1[k]->widthStep, c1_dev[ori]+k*c1_pitch*h_0, c1_pitch, w, h,   cudaMemcpyDeviceToHost);


          cvSaveImage(tmp, c1[k]);
        }
      }


    }











  cvNamedWindow("l", CV_WINDOW_AUTOSIZE);
  cvShowImage("l", c1[0]);
  cvWaitKey(0);

  cvDestroyWindow("l");

  cvReleaseImage(&im);


  FreeAll(w_dev, h_dev, pyr_dev, s1_dev, c1_dev, patch4x4, patch8x8, s2_dev);

  return 0;
}




